using System;
using System.Text.RegularExpressions;

namespace ValidationFramework.Specifications
{
    /// <summary>
    /// Performs a Regular Expression validation.
    /// </summary>
    /// <remarks>If the value being validated is null the rule will evaluate to true.</remarks>
    /// <seealso cref="RegexRuleConfigReader"/>
	/// <seealso cref="RegexRuleAttribute"/>
#if (!SILVERLIGHT)
    [Serializable]
#endif
    public class MatchesRegexSpec : SpecificationBase
    {

        // --- Constructors

        /// <remarks>
        /// The following are defaulted
        /// <list type="bullet">
        /// <item>
        /// <see cref="RegexOptions"/> to the <see cref="System.Text.RegularExpressions.RegexOptions.None"/>.
        /// </item>
        /// </list>
        /// </remarks>
        /// <param name="validationExpression">The regular expression pattern to match.</param>
        /// <exception cref="ArgumentNullException"><paramref name="validationExpression"/> is a null reference.</exception>
        /// <exception cref="ArgumentException"><paramref name="validationExpression"/> is <see cref="string.Empty"/>.</exception>
        public MatchesRegexSpec(string validationExpression)
			:this(validationExpression, RegexOptions.None)
        {

        }

        /// <param name="validationExpression">The regular expression pattern to match.</param>
        /// <param name="regexOptions">A bitwise OR combination of <see cref="System.Text.RegularExpressions.RegexOptions"/> enumeration values.</param>
        /// <exception cref="ArgumentNullException"><paramref name="validationExpression"/> is a null reference.</exception>
        /// <exception cref="ArgumentException"><paramref name="validationExpression"/> is <see cref="string.Empty"/>.</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="regexOptions"/> is out of the accepted range.</exception>
        public MatchesRegexSpec(string validationExpression, RegexOptions regexOptions)
        {
            Guard.ArgumentNotNullOrEmptyString(validationExpression, "validationExpression");
            if ((regexOptions < RegexOptions.None) || ((((int)regexOptions) >> 10) != 0))
            {
                throw new ArgumentOutOfRangeException("regexOptions");
            }

			ValidationExpression = validationExpression;
			RegexOptions = regexOptions;
        }


		// --- Properties

        /// <summary>
        /// Gets the regular expression pattern to match.  
        /// </summary>
        public string ValidationExpression
        {
			get;
			private set;
        }

        ///<summary>
        /// Gets A bitwise OR combination of <see cref="System.Text.RegularExpressions.RegexOptions"/> enumeration values.
        ///</summary>
        public RegexOptions RegexOptions
        {
			get;
			private set;
        }


        // --- Methods

		/// <inheritdoc />
		public override bool IsSatisfiedBy(object target)
		{
			if (target == null)
			{
				return true;
			}
			else
			{
				var s = (string)target;
				if (s.Length == 0)
				{
					return true;
				}
				else
				{
					var m = Regex.Match(s, ValidationExpression, RegexOptions);
					return (m.Success && (m.Index == 0) && (m.Length == s.Length));
				}
			}
		}

		/// <inheritdoc />
		public override string DefaultMessage(string tokenizedMemberName, string descriptorType)
		{
			return string.Format("The {0} '{1}' is an invalid format.", descriptorType, tokenizedMemberName);
		}

		/// <inheritdoc />
		public override string ToString()
		{
			return string.Format("The value must match the regular expression '{0}'.", ValidationExpression);
		}

		/// <inheritdoc />
		public override bool CanBeSatisfiedBy(Type typeToCheck)
		{
			return typeToCheck == typeof(string);
		}

		/// <inheritdoc />
		public override bool IsEquivalentTo(ISpecification spec)
		{
			var regexSpecification = spec as MatchesRegexSpec;
			if (regexSpecification == null)
				return false;

            return regexSpecification.RegexOptions == RegexOptions && regexSpecification.ValidationExpression == ValidationExpression;
		}
	}
}